﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Concat
{
    class RuntimeException : Exception
    {
        public CallStack stack;

        public RuntimeException(string message, CallStack stack)
            : base(message.Replace("{loc}", stack.getLoc().ToString()))
        {
            this.stack = stack;
        }
    }

    class UndefinedWordException : RuntimeException
    {
        public IdentTerm term;

        public UndefinedWordException(IdentTerm term, CallStack stack)
            : base("Word '" + term.ident + "' is not defined at {loc}.", stack)
        {
            this.term = term;
        }
    }
    class StackUnderflowException : RuntimeException
    {
        public IdentTerm term;

        public StackUnderflowException(IdentTerm term, CallStack stack)
            : base("Not enough values on stack to call word at {loc}.", stack)
        {
            this.term = term;
        }
    }
    class RecursionException : RuntimeException
    {
        public RecursionException(CallStack stack)
            : base("Call stack has overflowed due to recursion at {loc}.", stack)
        {
        }
    }
    class InvalidStackEffectException : RuntimeException
    {
        public InvalidStackEffectException(Type[] production, CallStack stack)
            : base("Invalid stack effect at {loc}; expected " 
                   + production.map(type => type.Name).interleave(",").concat() + ".",
                   stack)
        {
        }
        public InvalidStackEffectException(int consumption, CallStack stack)
            : base("Invalid stack effect at {loc}; expected consumption of " + consumption + " items.",
                   stack)
        {
        }
    }
    class InvalidArgTypesException : RuntimeException
    {
        public string word;
        public Type[] types;

        public InvalidArgTypesException(string word, Type[] types, CallStack stack)
            : base("Invalid argument types " 
                   + types.map(type => type.prettify()).interleave(", ").concat()
                   + " for word '" + word + "' at {loc}.", stack)
        {
            this.word = word;
            this.types = types;
        }
    }

    class InvalidMethodArgException : RuntimeException
    {
        public string word;
        public object obj;

        public InvalidMethodArgException(string word, object obj, CallStack stack)
            : base("Invalid argument type " + obj.GetType().prettify() + " for multimethod '"
                   + word + "' at {loc}.", stack)
        {
            this.word = word;
            this.obj = obj;
        }
    }
}